package cn.edu.gdu.quizpractice.controller.web;

import cn.edu.gdu.quizpractice.entity.ApiResult;
import cn.edu.gdu.quizpractice.entity.Bank;
import cn.edu.gdu.quizpractice.entity.ChoiceQuestion;
import cn.edu.gdu.quizpractice.service.IBankService;
import cn.edu.gdu.quizpractice.service.IChoiceQuestionService;
import cn.edu.gdu.quizpractice.service.IEmailService;
import cn.edu.gdu.quizpractice.service.IRedisCacheService;
import cn.edu.gdu.quizpractice.service.ExcelExportService;
import cn.edu.gdu.quizpractice.common.utils.JwtUtils;
import io.micrometer.core.annotation.Timed;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import jakarta.servlet.http.HttpServletResponse;

/**
 * @Author: Aubery
 * @Date: 2025-03-14-22:00
 * @Description:
 */

@RestController
@RequestMapping("/web/banks")
@Timed(value = "web/bankController",description = "BankController")
@Slf4j
@Transactional
@EnableScheduling
public class BankController {

    @Autowired
    private IBankService bankService;

    @Autowired
    private IEmailService emailService;
    
    @Autowired
    private IRedisCacheService redisCacheService;

    @Value("${admin.email}")
    private String adminEmail;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private IChoiceQuestionService choiceQuestionService;

    @Autowired
    private ExcelExportService excelExportService;

    @PostMapping("/updateTotal")
    public ApiResult updateTotal(Integer id, Integer total) {
        boolean result = bankService.updateTotal(id, total);
        if (result) {
            // 更新成功后，删除相关缓存
            redisCacheService.delete("bank:id:" + id);
            redisCacheService.delete("banks:all");
            return ApiResult.success();
        } else {
            return ApiResult.fail("更新失败");
        }
    }

    @GetMapping("/findTotalByName")
    public ApiResult findTotalById(@RequestParam String name) {
        // 定义缓存key
        String cacheKey = "bank:name:" + name + ":total";
        
        // 尝试从缓存获取
        Object cachedTotal = redisCacheService.get(cacheKey);
        if (cachedTotal != null) {
            log.info("从缓存获取题库总数: {}", cachedTotal);
            return ApiResult.success(cachedTotal);
        }
        
        // 缓存未命中，查询数据库
        log.info("缓存未命中，从数据库获取题库总数");
        Bank totalByName = bankService.findTotalByName(name);
        if (totalByName == null) {
            // 缓存空结果（5分钟）
            redisCacheService.set(cacheKey + ":empty", "null", 5, TimeUnit.MINUTES);
            return ApiResult.fail("不存在该题库");
        } else {
            // 缓存查询结果（30分钟）
            redisCacheService.set(cacheKey, totalByName.getTotal(), 30, TimeUnit.MINUTES);
            return ApiResult.success(totalByName.getTotal());
        }
    }

    @GetMapping("/findAll")
    public ApiResult findAll() {
        // 先清除缓存，确保每次获取最新数据
        String cacheKey = "banks:all";
        redisCacheService.delete(cacheKey);
        
        // 直接从数据库查询最新数据
        log.info("从数据库获取所有题库");
        List<Bank> banks = bankService.findAll();
        
        // 处理数据并返回
        if (banks != null) {
            // 创建简化版的题库列表
            List<Map<String, Object>> simplifiedBanks = banks.stream()
                .map(this::simplifyBank)
                .collect(Collectors.toList());
            
            // 更新缓存
            redisCacheService.set(cacheKey, simplifiedBanks, 1, TimeUnit.MINUTES);
            return ApiResult.success(simplifiedBanks);
        } else {
            return ApiResult.fail("查询失败");
        }
    }

    @GetMapping("/{bankName}/questions")
    public ApiResult findQuestionsByBankId(@PathVariable String bankName) {
        try {
            // 1. 首先清除可能存在的旧缓存
            String cacheKey = "bank:name:" + bankName + ":questions";
            redisCacheService.delete(cacheKey);
            
            // 2. 查询数据库
            log.info("从数据库获取题库问题: {}", bankName);
            Object rawQuestions = bankService.findQuestionsByBankName(bankName);
            
            // 3. 添加调试日志，查看返回的实际类型
            log.info("查询结果类型: {}", rawQuestions != null ? rawQuestions.getClass().getName() : "null");
            
            // 4. 根据返回类型进行适当处理
            List<Map<String, Object>> simplifiedQuestions = new ArrayList<>();
            
            if (rawQuestions instanceof List) {
                List<?> questionList = (List<?>) rawQuestions;
                if (!questionList.isEmpty()) {
                    log.info("列表中元素类型: {}", questionList.get(0).getClass().getName());
                    
                    // 处理不同的元素类型
                    for (Object item : questionList) {
                        if (item instanceof ChoiceQuestion) {
                            simplifiedQuestions.add(convertChoiceQuestionToMap((ChoiceQuestion) item));
                        } else if (item instanceof Map) {
                            // 已经是Map类型，直接添加
                            simplifiedQuestions.add((Map<String, Object>) item);
                        }
                    }
                }
            }
            
            // 5. 将结果存入缓存
            if (!simplifiedQuestions.isEmpty()) {
                redisCacheService.set(cacheKey, simplifiedQuestions, 15, TimeUnit.MINUTES);
            }
            
            return ApiResult.success(simplifiedQuestions);
        } catch (Exception e) {
            log.error("查询题库问题出错: {}", e.getMessage(), e);
            return ApiResult.fail("查询题库问题出错: " + e.getMessage());
        }
    }

    // 帮助方法 - 将ChoiceQuestion转换为Map
    private Map<String, Object> convertChoiceQuestionToMap(ChoiceQuestion question) {
        Map<String, Object> map = new HashMap<>();
        map.put("cquestionId", question.getCquestionId());
        map.put("cqContent", question.getCqContent());
        map.put("optionA", question.getOptionA());
        map.put("optionB", question.getOptionB());
        map.put("optionC", question.getOptionC());
        map.put("optionD", question.getOptionD());
        map.put("correctAnswer", question.getCorrectAnswer());
        map.put("analysis", question.getAnalysis());
        map.put("difficulty", question.getDifficulty());
        map.put("isMultiple", question.getIsMultiple());
        
        // 处理分类信息
        if (question.getCategoryName() != null) {
            map.put("categoryName", question.getCategoryName().toString());
        }
        
        return map;
    }

    /**
     * 添加题库(需要超级管理员权限才能添加!!!)
     * @param bank
     * @return
     */
    @PreAuthorize("hasAuthority('ROLE_SUPER_ADMIN')")
    @PostMapping("/add")
    public ApiResult add(@RequestBody Bank bank) {
        log.info("添加题库:{}", bank.toString());

        emailService.send(adminEmail, "题库添加通知", "题库已添加，题库名为:" + bank.getName());
        boolean result = bankService.save(bank);
        if (result) {
            // 添加成功后，删除相关缓存
            redisCacheService.delete("banks:all");
            redisCacheService.delete("bank:count");
            return ApiResult.success("添加成功");
        } else {
            return ApiResult.fail("添加失败");
        }
    }

    /**
     * 删除题库(需要超级管理员权限才能删除!!!)
     * @param bankId
     * @return
     */
    @PreAuthorize("hasAuthority('ROLE_SUPER_ADMIN')")
    @DeleteMapping("/{bankId}")
    public ApiResult deleteBank(@PathVariable Integer bankId, @RequestHeader("Authorization")String token) {
        log.info("删除题库:{}", bankId);
        String usernameFromToken = jwtUtils.getUsernameFromToken(token);
        Bank bankById = bankService.findBankById(bankId);
        if (bankById == null) {
            emailService.send(adminEmail, "题库删除通知", "题库不存在，题库名为:" + bankId+",操作人员为:"+usernameFromToken);
        } else {
            emailService.send(adminEmail, "题库删除通知", "题库已删除，题库名为:" + bankById.getName()+",操作人员为:"+usernameFromToken);
            // 删除相关缓存
            redisCacheService.delete("bank:id:" + bankId);
            redisCacheService.delete("bank:name:" + bankById.getName() + ":total");
            redisCacheService.delete("bank:name:" + bankById.getName() + ":questions");
        }

        choiceQuestionService.deleteByBankId(bankId);

        boolean result = bankService.removeById(bankId);
        if (result) {
            // 删除成功后，删除列表缓存
            redisCacheService.delete("banks:all");
            redisCacheService.delete("bank:count");
            redisCacheService.delete("bank:total:count");
            return ApiResult.success("删除成功");
        } else {
            return ApiResult.fail("删除失败");
        }
    }

    /**
     * 修改题库
     * @param bank
     * @return
     */
    @PostMapping("/update")
    public ApiResult updateByID(@RequestBody Bank bank, @RequestHeader("Authorization") String token) {
        boolean result = bankService.updateByBankId(bank);
        String usernameFromToken = jwtUtils.getUsernameFromToken(token);
        if (result) {
            // 修改成功后，删除相关缓存
            redisCacheService.delete("bank:id:" + bank.getBId());
            redisCacheService.delete("bank:name:" + bank.getName() + ":total");
            redisCacheService.delete("bank:name:" + bank.getName() + ":questions");
            redisCacheService.delete("banks:all");
            emailService.send(adminEmail, "题库修改通知", "题库已修改，题库名为:" + bank.getName() + ",操作人员为:" + usernameFromToken);
            return ApiResult.success("修改成功");
        } else {
            emailService.send(adminEmail, "题库修改通知", "题库修改失败，题库名为:" + bank.getName() + ",操作人员为:" + usernameFromToken);
            return ApiResult.fail("修改失败");
        }
    }

    @GetMapping("/findById/{id}")
    public ApiResult findById(@PathVariable Integer id) {
        // 定义缓存key
        String cacheKey = "bank:id:" + id;
        
        // 尝试从缓存获取
        Object cachedBank = redisCacheService.get(cacheKey);
        if (cachedBank != null) {
            log.info("从缓存获取题库: {}", id);
            return ApiResult.success(cachedBank);
        }
        
        // 缓存未命中，查询数据库
        log.info("缓存未命中，从数据库获取题库: {}", id);
        Bank bank = bankService.findBankById(id);
        
        if (bank == null) {
            // 缓存空结果（5分钟）
            redisCacheService.set(cacheKey + ":empty", "null", 5, TimeUnit.MINUTES);
            return ApiResult.fail("题库不存在");
        } else {
            // 缓存查询结果（30分钟）
            Map<String, Object> simplifiedBank = simplifyBank(bank);
            redisCacheService.set(cacheKey, simplifiedBank, 30, TimeUnit.MINUTES);
            return ApiResult.success(bank);
        }
    }

    /**
     * 审核题库
     */
    @PostMapping("/audit")
    public ApiResult audit(@RequestBody Bank bank) {
        System.out.println(bank.toString());
        boolean result = bankService.audit(bank);
        if (result) {
            // 审核成功后，删除相关缓存
            redisCacheService.delete("bank:id:" + bank.getBId());
            redisCacheService.delete("bank:name:" + bank.getName() + ":total");
            redisCacheService.delete("banks:all");
            emailService.send(adminEmail, "题库审核通知", "题库已审核，题库名为:" + bank.getName());
            return ApiResult.success("审核成功");
        } else {
            emailService.send(adminEmail, "题库审核通知", "题库审核失败，题库名为:" + bank.getName());
            return ApiResult.fail("审核失败");
        }
    }

    /**
     * 定时任务：每天上午十点和下午两点发送未审核的题库信息给超级管理员
     * 将未审核的题库信息发送给超级管理员
     */
    //@Scheduled(cron = "0 0/1 * * * ?") // 每分钟执行一次
    @Scheduled(cron = "0 0 10,14 * * ?") //  每天上午10点和下午14点执行一次
    public void sendUnAuditedBanksToSuperAdmin() {
        List<Bank> unAuditedBanks = bankService.showUnAuditedBanks();
        StringBuilder sb = null;
        if (unAuditedBanks.size() > 0) {
            sb = new StringBuilder();
            for (Bank bank : unAuditedBanks) {
                sb.append("题库名称:").append(bank.getName()).append("\n");
                sb.append("题库数量:").append(bank.getTotal()).append("\n");
                sb.append("是否已审核:").append(bank.getIsReviewed()?"是":"否").append("\n\n");
            }
            sb.append("共"+unAuditedBanks.size()+"个题库未审核,请您及时审核");
            emailService.send(adminEmail, "题库审核通知", sb.toString());
            log.info("已发送未审核题库信息给超级管理员"+sb.toString());
        }
    }

    /**
     * 获取题库数量
     * @return
     */
    @GetMapping("/getBankCount")
    public ApiResult getBankCount() {
        // 定义缓存key
        String cacheKey = "bank:count";
        
        // 尝试从缓存获取
        Object cachedCount = redisCacheService.get(cacheKey);
        if (cachedCount != null) {
            log.info("从缓存获取题库数量");
            return ApiResult.success(cachedCount);
        }
        
        // 缓存未命中，查询数据库
        log.info("缓存未命中，从数据库获取题库数量");
        int bankCount = bankService.getBankCount();
        
        // 缓存查询结果（5分钟）
        redisCacheService.set(cacheKey, bankCount, 5, TimeUnit.MINUTES);
        return ApiResult.success(bankCount);
    }

    /**
     * 导出题库题目为Excel
     * @param bankName 题库名称
     * @param response HTTP响应对象
     * @throws IOException 如果导出过程中出现IO异常
     */
    @GetMapping("/exportExcel/{bankName}")
    public void exportExcel(@PathVariable String bankName, HttpServletResponse response) throws IOException {
        log.info("开始导出题库 {} 的题目为Excel", bankName);

        // 使用ExcelExportService导出Excel
        excelExportService.exportBankQuestionsToExcel(bankName, response);
    }


    /**
     * 获取所有题库中的题目总数
     * @return
     */
    @GetMapping("/getTotalCount")
    public ApiResult getTotalCount() {
        // 定义缓存key
        String cacheKey = "bank:total:count";
        
        // 尝试从缓存获取
        Object cachedCount = redisCacheService.get(cacheKey);
        if (cachedCount != null) {
            log.info("从缓存获取题目总数");
            return ApiResult.success(cachedCount);
        }
        
        // 缓存未命中，查询数据库
        log.info("缓存未命中，从数据库获取题目总数");
        int totalCount = bankService.getTotalCount();
        
        // 缓存查询结果（5分钟）
        redisCacheService.set(cacheKey, totalCount, 5, TimeUnit.MINUTES);
        return ApiResult.success(totalCount);
    }
    
    /**
     * 将Bank对象转换为简化版Map
     */
    private Map<String, Object> simplifyBank(Bank bank) {
        Map<String, Object> map = new HashMap<>();
        map.put("bId", bank.getBId());
        map.put("name", bank.getName());
        map.put("total", bank.getTotal());
        map.put("isReviewed", bank.getIsReviewed());
        // 可以根据需要添加其他字段
        return map;
    }

}
